import { WidgetBean } from '@indexea/sdk';
import { SearchContext, SearchFilter } from '@indexea/sdk/context';
import { WidgetLayout } from '@indexea/sdk/layout';
import {
  createContext,
  Dispatch,
  ReactNode,
  SetStateAction,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';
import { Indexea } from '../openapi';

export type IndexeaSearchResults = object;

export type UseInitIndexeaSearchProps = {
  indexea: Indexea
  widget: WidgetBean
}

export type IndexeaInputAttr = {
  type: 'input',
  button: boolean,
  complete: string,
  complete_count: string | number,
  complete_url_field: string | number,
  logo: boolean,
  placeholder: ReactNode,
  query: string,
  suggest: boolean,
}

export type HotWordsAttr = {
  type: 'hotwords',
  scope: string,
  show_count: number | string,
}

export type IndicesAttr = {
  type: 'indices',
  show_count: boolean,
} & {
  [k: string]: string,
}

export type AggregationAttr = {
  type: 'aggregation',
  show_count: boolean,
} & {
  [k: string]: string,
}


export type IndexeaLayoutData = {
  data: WidgetLayout,
  input: IndexeaInputAttr,
  hotWords: HotWordsAttr,
  indices: IndicesAttr,
  aggregation: AggregationAttr
}

const defaultLayout = '<layout/>';

export type IndexeaSearchContextType = {
  context: SearchContext,
  results: IndexeaSearchResults | undefined,
  setResults: Dispatch<SetStateAction<IndexeaSearchResults | undefined>>,
  inQuery: boolean,
  defaultLogo: string,
  selectLogo: (showLogo?: boolean) => string,
  layout: IndexeaLayoutData,
  search: (q: string, queryId?: number) => void,
  loading: boolean,
}

// @ts-ignore context default value
const IndexeaSearchContext = createContext<IndexeaSearchContextType>({});

export const IndexeaSearchProvider = IndexeaSearchContext.Provider;

function prepareWidgetLayout(data: WidgetLayout): IndexeaLayoutData {
  return {
    data,
    input      : data.getAttributes('input'),
    hotWords   : data.getAttributes('hotwords'),
    indices    : data.getAttributes('indices'),
    aggregation: data.getAttributes('aggregation'),
  };
}

// 因为 SearchContext 已经封装好了，所以我就不去改动了，这里只作为对 SearchContext 的一个代理暴露
export function useInitIndexeaSearch({
  widget,
  indexea,
}: UseInitIndexeaSearchProps): IndexeaSearchContextType {
  const locationSearch = window.location.search;

  const searchRef = useRef<string>(locationSearch);
  const layoutRef = useRef<string | undefined>(defaultLayout);

  //全局搜索上下文
  const [context, setContext] = useState<SearchContext>(
    SearchContext.fromUrl(widget, locationSearch),
  );
  //搜索结果
  const [results, setResults] = useState<IndexeaSearchResults | undefined>(undefined);
  const [layout, setLayout] = useState<IndexeaLayoutData>(prepareWidgetLayout(WidgetLayout.parse(defaultLayout)));
  const [loading, setLoading] = useState(false);
  const [logo, setLogo] = useState<string>('/static/indexea.png');

  const beginSearch = useCallback(function beginSearch() {
    if (!context?.q) return;
    // let url = context.toUrl();
    // history.pushState({}, '', '?' + url);
    var args: any = context.filters.reduce((obj: any, f: SearchFilter) => {
      obj[f.name] = obj[f.name]
        ? Array.isArray(obj[f.name])
          ? obj[f.name].concat(f.value)
          : [obj[f.name], f.value]
        : f.value;
      return obj;
    }, {});
    args.sort_by_f = context.sort;
    // setResults(undefined);
    setLoading(true);
    indexea
      .search(
        context.query,
        context.q,
        args,
        context.getFromIndex(),
        context.itemsPerPage,
      )
      .then(setResults)
      .catch((err: any) => {
        console.error(err);
        setResults(undefined);
      })
      .finally(() => setLoading(false))
    ;
  }, [context]);

  const updateContext = useCallback((search: string) => {
    if (searchRef.current !== search) {
      searchRef.current = search;
      const newContext = SearchContext.fromUrl(widget, search);
      setContext(newContext);
    }
  }, []);

  const search = useCallback(function search(q: string, queryId?: number) {
    // loading 就不去加载
    if (loading) return;
    const newContext = new SearchContext(widget, q, 1, [], queryId == null ? context.query: queryId, context.sort);
    const url = newContext.toUrl();
    const search = !url ? '' : '?' + url;
    history.pushState({}, '', '?' + url);
    updateContext(search);
  }, [beginSearch, context, loading]);

  const listenPopState = useCallback(function listenPopState(event: PopStateEvent) {
    // @ts-ignore event location
    updateContext(event.currentTarget.location.search);
  }, [updateContext]);

  useEffect(() => {
    updateContext(window.location.search);
  }, [window.location.search]);

  useEffect(() => {
    beginSearch();
  }, [context]);

  useEffect(() => {
    if (context.widget.layout !== layoutRef.current) {
      layoutRef.current = context.widget.layout;
      setLayout(prepareWidgetLayout(WidgetLayout.parse(context.widget.layout || defaultLayout)));
    }
  }, [context.widget.layout]);

  useEffect(() => {
    window.addEventListener('popstate', listenPopState);

    return () => window.removeEventListener('popstate', listenPopState);
  }, []);

  const getHotWordsShowCount = useCallback(() => {
    return (layout.hotWords.show_count as number) || 5;
  }, [layout]);

  const selectLogo = useCallback((showLogo = true) => {
    if (showLogo && !!layout.input.logo) {
      return logo;
    }
    return '';
  }, [layout, logo]);

  return {
    context,
    results, setResults,
    inQuery    : !!context.q,
    defaultLogo: logo,
    selectLogo,
    layout,
    search,
    loading,
  };
}

export function useIndexeaSearch(): IndexeaSearchContextType {
  return useContext<IndexeaSearchContextType>(IndexeaSearchContext);
}

// 以下为从首页 index.tsx 搬过来的方法，而且未实现的方法体
/**
 function switchQuery(query: number) {
    if (context.query != query) {
      context.query = query;
      context.filters = [];
      setContext(context);
      beginSearch();
    }
  }

 function sort(s: string) {
    context.sort = s;
    setContext(context);
    beginSearch();
  }

 function changePage(p: number) {
    context.page = p;
    setContext(context);
    beginSearch();
  }

 function addFilter(f: SearchFilter) {
    if (f.multiple || !context.filters.find(sf => sf.name == f.name)) {
      context.filters.push(f);
    } else {
      context.filters = context.filters.map(sf => (sf.name == f.name ? f : sf));
    }
    setContext(context);
    beginSearch();
  }

 function deleteFilter(f: SearchFilter) {
    context.filters = context.filters.filter(
      tf => tf.name != f.name || tf.value != f.value,
    );
    setContext(context);
    beginSearch();
  }

 function clickHit(_id: string) {
    indexea.click(results.action, _id);
  }
 */
